<?php
 
/**
 *  Quad_Cipher class
 *  
 *  @author - Ken Snyder
 *  @version - 3.0 (updated Dec 2009)
 *<pre>
 *  ENCRYPT/DECRYPT
 *  - Encrypts strings using government-grade encryption based on a passed key
 *  - Stores the generated iv string with the encrypted data
 *  - Good for passwords, credit-card numbers, or any potentially hackable information
 *  - Requires mcrypt library be installed
 *
 *	EXAMPLE USAGE
 *  <code>$sEncrypted = Quad_Cipher::encrypt($sData, $sKey);</code>
 *
 *  for more info on encryption and php mcrypt extension go to the following web page:
 *  http://www.hudzilla.org/phpbook/read.php/17_3_1
 *</pre>
 */
abstract class Quad_Cipher
{

  /**
   *  encrypt data
   *
   *  @param string $sData  the data to encrypt (required)
   *  @param string $sKey  a key to lock the data (required); Be sure to use the same key for encryption and decryption
   *  @param string $sCipher  contant of cipher name (defaults to MCRYPT_RIJNDAEL_128)
   *  @param string $sBlockMode  contant or integer of cipher block mode (defaults to MCRYPT_MODE_CBC)
   *  @param bool   $bBase64  if true, encode final result into base 64
   *  @return string
   */
	public static function encrypt($sData, $sKey, $sCipher=MCRYPT_RIJNDAEL_128, $sBlockMode=MCRYPT_MODE_CBC, $bBase64=true) {
	  if (strlen($sData) == 0) return '';  
  
    srand((double) microtime() * 1000000); // seed for MCRYPT_RAND
    $sKey = sha1($sKey); // secure the key
    
    // Open module, and create initialization vector (iv)
    $oTd = mcrypt_module_open($sCipher, '', $sBlockMode, '');
    
    if (!$oTd) {
    	throw new Exception('Error opening mycrypt module. Unable to encrypt data.');
    }
    
    $sKey = substr($sKey, 0, mcrypt_enc_get_key_size($oTd));
    $iIvSize = mcrypt_enc_get_iv_size($oTd);
   	$sIv = mcrypt_create_iv($iIvSize, MCRYPT_RAND); 	
    // Initialize encryption module
    $iHandle = mcrypt_generic_init($oTd, $sKey, $sIv);
    
    if ($iHandle == -1) {
    	throw new Exception('Error initializing mycrypt module. Unable to encrypt data.');
    }
    
    // Encrypt data
    $sEncryptedData = mcrypt_generic($oTd, $sData);
    // append the initialization vector for easy decryption
    $sEncryptedData = $sEncryptedData . $sIv;
		// close module
    mcrypt_generic_deinit($oTd);
    mcrypt_module_close($oTd);
    // base64 encode
    if ($bBase64) {
    	$sEncryptedData = base64_encode($sEncryptedData);
    }
 
    return $sEncryptedData;
	}
 
  /**
   *  decrypt data (be sure to use the same key and options as when the data was encrypted)
   *
   *  @param string $sEncryptedData  the data to decrypt (required)
   *  @param string $sKey  the key used for encryption
   *  @param string $sCipher  contant of cipher name (defaults to MCRYPT_RIJNDAEL_128)
   *  @param string $sBlockMode  contant or integer of cipher block mode (defaults to MCRYPT_MODE_CBC)
   *  @param bool   $bBase64  if true, decode encrypted data from base 64
   *  @return string
   */
	public static function decrypt($sEncryptedData, $sKey, $sCipher=MCRYPT_RIJNDAEL_128, $sBlockMode=MCRYPT_MODE_CBC, $bBase64=true) {
	  if (strlen($sEncryptedData) == 0) return '';
  
    if ($bBase64) {
      $sEncryptedData = base64_decode($sEncryptedData);
    }
      
    $sKey = sha1($sKey); // same as encrypt method
    // Open module, and create iv using same param as above
    $oTd = mcrypt_module_open($sCipher, '', $sBlockMode, '');
    
	  if (!$oTd) {
    	throw new Exception('Error opening mycrypt module. Unable to decrypt data.');
    }    

    $sKey = substr($sKey, 0, mcrypt_enc_get_key_size($oTd));
    $iIvSize = mcrypt_enc_get_iv_size($oTd);
    if (strlen($sEncryptedData) <= $iIvSize ) {
      throw new Ss_Exception('Encrypted string is too short. Unable to decrypt data.');
    }
    
    $sIv = substr($sEncryptedData, strlen($sEncryptedData)-$iIvSize);
    $sEncryptedData = substr($sEncryptedData,0,strlen($sEncryptedData)-$iIvSize);
      
	  // Initialize encryption module
    $iHandle = mcrypt_generic_init($oTd, $sKey, $sIv);
    
    if ($iHandle == -1) {
    	throw new Exception('Error initializing mycrypt module. Unable to encrypt data.');
    }
    // Decrypt data
    $sDecryptedData = mdecrypt_generic($oTd, $sEncryptedData);
    // remove garbage whitespace left over
    $sDecryptedData = rtrim($sDecryptedData);
    // close module
    mcrypt_generic_deinit($oTd);
    mcrypt_module_close($oTd);
    return $sDecryptedData;
	}
 
}
